package stripsPlanner;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import stripsLanguage.Instance;
import stripsLanguage.Link;
import stripsLanguage.Predicate;
import stripsLanguage.Step;

public class SelectSubgoal {

	public SelectSubgoal() {}

	// klasa pomocnicza do zwracania danych
	protected class InnerClass {
		private List<Predicate> UnachievedPreconditions = null;
		private List<Step> UnachievedPreconditionsStep = null;
		public InnerClass(
			List<Predicate> unachievedPreconditions,
			List<Step> unachievedPreconditionsStep)
		{
			UnachievedPreconditions = unachievedPreconditions;
			UnachievedPreconditionsStep = unachievedPreconditionsStep;			
		}
		
		public List<Predicate> getUnachievedPreconditions() {return UnachievedPreconditions;}
		public List<Step> getUnachievedPreconditionsStep() {return UnachievedPreconditionsStep;}	   
	}

	// kolejna klasa pomocnicza do zwracania danych
	protected class InnerMinMaxClass {

		private int Min = 0;
		private int Max = 0;
		private List<Integer> Unknowns = null;
		private List<Integer> Sizes = null; //liczba instancji w predykacie
		
		public InnerMinMaxClass(
			int min,
			int max,
			List<Integer> unknowns,
			List<Integer> sizes)
		{
			Min = min;
			Max = max;
			Unknowns = unknowns;
			Sizes = sizes;
		}
		public int getMin() {return Min;}
		public int getMax() {return Max;}
		public List<Integer> getUnknowns() {return Unknowns;}
		public List<Integer> getSizes() {return Sizes;}		
	}
	
	/**
	 * zwraca kandydatow do zostania celem (niespelnione predykaty)
	 * @param steps
	 * @param links
	 * @return
	 * TEST
	 */
	protected InnerClass pickCandidates(List<Step> steps, List<Link> links)
	{
		List<Predicate> unachievedPreconditions = new ArrayList<Predicate>();
		List<Step> unachievedPreconditionsStep = new ArrayList<Step>();
		// pick all the unachieved preconditions and the steps they belong to
		for(Step step: steps)			
			for(Predicate pre: step.getPreconditions())
			{	
				boolean unachieved = true;
				for(Link link: links)
				{
					if(link.getFullilledPredicate().getIdOfPredicate() == pre.getIdOfPredicate())
					{
						unachieved = false;
						break;
					}					
				}
				
				// dodaj tylko, jesli co najmniej jedna instancja ma jakas wartosc przypisana w Binginds
				/*boolean value = false;
				for(Instance i: pre.getArguments())
					if(i.getId() > 0)
						value = true;
					NIE MOZNA, bo GO !
				*/
				if(unachieved) // && value)
				{
					unachievedPreconditions.add(pre);
					unachievedPreconditionsStep.add(step);
				}
			}
		return new InnerClass(
			unachievedPreconditions,
			unachievedPreconditionsStep);
	}
	
	/**
	 * klasa wybiera kandydatow, z ktorych zostanie wylosowany predykat do spelnienia
	 * @param unachievedPreconditions
	 * @return
	 * TEST
	 */
	protected InnerMinMaxClass countMinMax(List<Predicate> unachievedPreconditions)
	{
		List<Integer> unknowns = new ArrayList<Integer>();
		List<Integer> sizes = new ArrayList<Integer>(); //liczba instancji w predykacie
		for(Predicate p: unachievedPreconditions)
		{
			int u = 0;
			for(Instance i: p.getArguments())
				if(i.getId() < 0)
					++u;
			unknowns.add(u);
			sizes.add(p.getArguments().size());
		}
		
		/*System.out.println("Unknowns: ");
		for(int i: unknowns)
			System.out.print("," + i);
		System.out.println("");
		*/
		// znajdz minimalna liczbe nieznanych
		int min = unknowns.get(0);
		for(int i: unknowns)
			if(min > i)
				min = i;
		/*System.out.println("Minimalna liczba nieznanych: " + min);
		/
		System.out.println("Unknowns.size: ");
		for(int i: sizes)
			System.out.print("," + i);
		System.out.println("");
		*/
		// dla tych z minimalna liczba znajdz maksymalna liczbe znanych
		int max = 0;
		for(int i = 0; i < unknowns.size(); ++i)
			if(unknowns.get(i) == min)
				if(sizes.get(i) > max)
					max = sizes.get(i);
		
		return new InnerMinMaxClass(
			min,
			max,
			unknowns,
			sizes);
		//System.out.println("max liczba zmiennych dla minimalnej liczby nieznanych: " + max);
	}
	
	// zwraca cel, ale patrzy na liczbe predykatow bez instancji
	public Subgoal selectSubgoal(List<Step> steps, List<Link> links)
	{
		InnerClass ic = pickCandidates(steps,links);
		List<Predicate> unachievedPreconditions = ic.getUnachievedPreconditions();
		List<Step> unachievedPreconditionsStep = ic.getUnachievedPreconditionsStep(); 

		//System.out.println("mozna wybrac cel z " + unachievedPreconditions.size() + " elementow");
		InnerMinMaxClass imm = countMinMax(unachievedPreconditions);
		
		// wstaw do nowej list te unachieved, gdzie liczba nieznanych rowna min
		List<Predicate> unachievedPreconditions2 = new ArrayList<Predicate>();
		List<Step> unachievedPreconditionsStep2 = new ArrayList<Step>();
	
		for(int i = 0; i < unachievedPreconditions.size(); ++i)		
			if(imm.getUnknowns().get(i) == imm.getMin()
				&& imm.getSizes().get(i) == imm.getMax())
			{
				unachievedPreconditions2.add(unachievedPreconditions.get(i));
				unachievedPreconditionsStep2.add(unachievedPreconditionsStep.get(i));				
			}		
		
		//System.out.println("ostateczny wybor z " + unachievedPreconditions2.size() + " elementow");
		
		// pick one step from the list
		//int randomNr = new Random().nextInt(unachievedPreconditions2.size());
		int randomNr = 0; 
		//TODO wrocic do losowosci
		
		return new Subgoal(
			unachievedPreconditionsStep2.get(randomNr),
			unachievedPreconditions2.get(randomNr));
	}
	
}
